﻿namespace CatClient.Message.Spi.Heartbeat
{
    using CatClient;
    using CatClient.Message.Spi;
    using CatClient.Message.Spi.Heartbeat.Extend;
    using CatClient.Util;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;

    [XmlRoot("status")]
    public class NodeStatusInfo : IRefresh, IXmlSerializable
    {
        internal bool HaveAcessRight;
        private readonly Stopwatch _stopwatch;
        private readonly Stopwatch _diskStopwatch;
        private XmlSerializerNamespaces ns;
        private readonly Dictionary<string, Action> _refreshables;

        public NodeStatusInfo()
        {
            this.ns = new XmlSerializerNamespaces();
            this._stopwatch = new Stopwatch();
            this._diskStopwatch = new Stopwatch();
        }

        public NodeStatusInfo(IMessageStatistics statistics) : this()
        {
            this._stopwatch.Start();
            this.RuntimeInfo = new RuntimeInfo();
            this.OSInfo = new OSInfo();
            this.DiskInfoList = new List<DiskInfo>();
            this.MemoryInfo = new MemoryInfo();
            this.ThreadInfo = new ThreadInfo();
            this.MessageInfo = new MessageInfo(statistics);
            this.HeartbeatExtensions = new List<HeartbeatExtention>();
            this.Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
            this.ns.Add("", "");
            this._refreshables = new Dictionary<string, Action>();
            this._stopwatch.Stop();
            LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Info($"Heartbeat {"NodeStatusInfo"} init finish. {this._stopwatch.ElapsedMilliseconds}ms");
        }

        private void DiskInfoRefresh()
        {
            this._diskStopwatch.Restart();
            LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Info("Heartbeat DiskInfo Refresh.");
            this.DiskInfoList.Clear();
            DriveInfo.GetDrives().ToList<DriveInfo>().ForEach(delegate (DriveInfo drive) {
                if (drive.IsReady)
                {
                    DiskInfo item = new DiskInfo {
                        Id = drive.Name,
                        Free = drive.AvailableFreeSpace,
                        Total = drive.TotalSize,
                        Use = drive.TotalSize - drive.AvailableFreeSpace
                    };
                    this.DiskInfoList.Add(item);
                }
            });
            this._diskStopwatch.Stop();
            LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Info($"Heartbeat {"NodeStatusInfo"} {"DiskInfoRefresh"} finish. {this._stopwatch.ElapsedMilliseconds}ms");
        }

        public XmlSchema GetSchema() => 
            null;

        public void ReadXml(XmlReader reader)
        {
        }

        public void Refresh()
        {
            if (this._refreshables.Count == 0)
            {
                Dictionary<string, Action> dictionary = this._refreshables;
                lock (dictionary)
                {
                    if (this._refreshables.Count == 0)
                    {
                        this.RefreshableInit();
                    }
                }
            }
            this._stopwatch.Restart();
            this.Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
            foreach (KeyValuePair<string, Action> pair in this._refreshables)
            {
                try
                {
                    pair.Value();
                }
                catch (Exception exception)
                {
                    LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Error($"[{pair.Key} Heartbeat Refersh Error]" + exception.Message, exception.ToCatException());
                }
            }
            this._stopwatch.Stop();
            LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Info($"Heartbeat {"NodeStatusInfo"} refresh finish. {this._stopwatch.ElapsedMilliseconds}ms");
        }

        private void RefreshableInit()
        {
            this._stopwatch.Restart();
            this._refreshables.Clear();
            RuntimeInfo runtimeInfo = this.RuntimeInfo;
            this._refreshables.Add("RuntimeInfo", new Action(runtimeInfo.Refresh));
            OSInfo oSInfo = this.OSInfo;
            this._refreshables.Add("OSInfo", new Action(oSInfo.Refresh));
            this._refreshables.Add("DiskInfo", new Action(this.DiskInfoRefresh));
            MemoryInfo memoryInfo = this.MemoryInfo;
            this._refreshables.Add("MemoryInfo", new Action(memoryInfo.Refresh));
            ThreadInfo threadInfo = this.ThreadInfo;
            this._refreshables.Add("ThreadInfo", new Action(threadInfo.Refresh));
            MessageInfo messageInfo = this.MessageInfo;
            this._refreshables.Add("MessageInfo", new Action(messageInfo.Refresh));
            foreach (HeartbeatExtention extention in this.HeartbeatExtensions)
            {
                this._refreshables.Add(extention.GetType().Name, new Action(extention.Refresh));
            }
            this._stopwatch.Stop();
            LogInfoWriter.GetInstance("Cat", 0, LogInfoWriter.LogLayout.Default).Info($"Heartbeat {"NodeStatusInfo"} {"RefreshableInit"} finish. {this._stopwatch.ElapsedMilliseconds}ms");
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString("timestamp", this.Timestamp);
            new XmlSerializer(typeof(RuntimeInfo)).Serialize(writer, this.RuntimeInfo, this.ns);
            new XmlSerializer(typeof(OSInfo)).Serialize(writer, this.OSInfo, this.ns);
            writer.WriteStartElement("disk");
            this.DiskInfoList.ForEach(item => new XmlSerializer(typeof(DiskInfo)).Serialize(writer, item, this.ns));
            writer.WriteEndElement();
            new XmlSerializer(typeof(MemoryInfo)).Serialize(writer, this.MemoryInfo, this.ns);
            new XmlSerializer(typeof(ThreadInfo)).Serialize(writer, this.ThreadInfo, this.ns);
            new XmlSerializer(typeof(MessageInfo)).Serialize(writer, this.MessageInfo, this.ns);
            this.HeartbeatExtensions.ForEach(delegate (HeartbeatExtention item) {
                writer.WriteStartElement("extension");
                writer.WriteAttributeString("id", item.Id);
                if (!string.IsNullOrEmpty(item.AdditionInfo))
                {
                    writer.WriteAttributeString("addition", item.AdditionInfo);
                }
                foreach (KeyValuePair<string, double> pair in item.Dict)
                {
                    writer.WriteStartElement("extensionDetail");
                    writer.WriteAttributeString("id", pair.Key.ToString());
                    writer.WriteAttributeString("value", pair.Value.ToString());
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            });
        }

        [XmlAttribute("timestamp")]
        public string Timestamp { get; set; }

        public RuntimeInfo RuntimeInfo { get; set; }

        public OSInfo OSInfo { get; set; }

        public List<DiskInfo> DiskInfoList { get; set; }

        public MemoryInfo MemoryInfo { get; set; }

        public ThreadInfo ThreadInfo { get; set; }

        public MessageInfo MessageInfo { get; set; }

        public List<HeartbeatExtention> HeartbeatExtensions { get; set; }
    }
}

